package event;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;


import Entities.*;
import Segments.Segment;
import java.util.Random;

public class CarArrival extends Event {

    int EMPTY = -1;
    int newEvent = EMPTY;
    double evaluatedPosition;
    double deltaT;
    double deltaP;
    double arrivalPosition;
    double arrivalTime;
    double startTime;
    double PosibledeltaT;
    double PosibledeltaP;
    double PosiblearrivalPosition;
    double PosiblearrivalTime;
    Event nextEvent;
    int VERDE = 1;
    int ROJO = 0;
    int AMBAR = 3;
  
    Event root;
    Segment segment;
    static String N = System.getProperty("line.separator");
  
    private boolean LOG = false;
    private int poisson;
    private double t;
    private Car car;

    public CarArrival() {
    }
    //debo recibir segment tambien

    public CarArrival(Segment segment) {


        this.root = segment.getRoot();
        this.segment = segment;


    }



    @Override
    public String toString() {
        return "CarArrival";
    }

 

    public int Poisson(double lambda) {
        Random rand = new Random();
        int k = 0;
        double A = 1;
        double dummy;
        for (k = 1; k <= 1000; k++) {
            dummy = 0;
            while (dummy <= 0 || dummy >= 1) {
                dummy = rand.nextDouble();
            }
            A = A * dummy;
            if (A < Math.exp(-lambda)) {
                return k - 1;
            }
        }
        return k - 1;
    }

    @Override
    public void run() {
        //Escribir el evento ejecutado en un archivo
        myLogger.debug("Executing At: " + this.evTime() + " event type:  " + this.getEventType());
       
        poisson = Poisson(5.0);
        while(poisson == 0)
        poisson = Poisson(5.0);

        t = this.eventTime + poisson;
        //System.out.println("Se puso un evento AE a las "+t+"para el carrito "+n);
        // ponerEvento(int segment,double evTime,int typeEvent, Object objectId, double eventPosition,int desiredStatus, int lane){
        car = new Car(this.myStreetControllerAgent.n, 0.0, "car " + this.myStreetControllerAgent.n, 2.0, 40000.0, 2.0);

        this.myStreetControllerAgent.putEvent(this.segment.getId(), t, AE, car, 0, AMBAR, 0);
        this.myStreetControllerAgent.n++;

         car = (Car) this.objectId();

        // Colocar el nuevo veh�culo en el segmento, en la pos. 0.


        if (!this.segment.isFull(this.evTime())) {

            try {
                FileOutputStream out = new FileOutputStream("c:/temp/log.txt", true);

                PrintWriter miArchivo = new PrintWriter(out);
                miArchivo.write(this.objectId() + ",");
                miArchivo.write(this.evTime() + ",");
                miArchivo.write(segment.getId() + ",");
                miArchivo.write(this.evPosition() + ",");
                miArchivo.write(this.getEventType() + ",");

                miArchivo.println();
                miArchivo.close();

            } catch (IOException ioe) {
                myLogger.error("Error en la creacion del archivo log.txt");
                throw new UnsupportedOperationException("The output file log cant be created !!");
            }

            this.segment.putCar((Car) objectId(), new Double(0.0));
            nextEvent = root.siguiente;
            // Obtengo el primer evento en la FEL (SQS) de este segmento.


            evaluatedPosition = segment.getSize();
            myLogger.debug("Size of the street> "+this.segment.getId()+"is > "+segment.getSize());

            while (nextEvent != root) {



                if (nextEvent.getEventType() != LCE && nextEvent.getLane() == getLane()) // No es un cambio de luz.
                {
                    if (nextEvent.evPosition() <= evaluatedPosition) {

                        if (nextEvent.evPosition() > this.evPosition()) {

                            if (nextEvent.getEventType() == BE || nextEvent.getEventType() == WBE || nextEvent.getEventType() == CE) {


                                myLogger.debug(car.getId() + " Estoy viendo un " + nextEvent.getEventType() + "a las " + nextEvent.evTime() + " en la pos " + nextEvent.evPosition());

                                /*
                                 * Las unidades de LA VELOCIDAD (standard velocity)
                                 * esta dada en metros por hora, y los resultados
                                 * son en segundos
                                 */

                                arrivalPosition = nextEvent.evPosition() - car.getDisMinSafe();
                                deltaP = arrivalPosition - this.evPosition();
                                deltaT = (deltaP * 3600) / car.getVelocity();
                                arrivalTime = deltaT + this.evTime();

                                /*
                                 * aqui hay dos casos para cuando llego despues de la hora del evento que estoy analizando
                                 * o llego antes.
                                 * si llego despues se supone que el evento que estoy analizando ya paso, asi que no me importa si fue un stop
                                 * real o de warning, asi que pongo un stop tipo warning para analizar
                                 * Si llego antes de que suceda el evento, verifico que tipo de stop es y tomo decisiones.
                                 */
                                if (arrivalTime >= nextEvent.evTime()) {  // Posiblemente cuando llegue al punto de colision, el otro
                                    //  vehiculo ya se fue por eso es solo un warning.

                                    newEvent = WSE;
                                    startTime = arrivalTime;

                                } else {


                                    if (nextEvent.getEventType() == BE) { // se el de adelante esta parado por mucho tiempo,
                                        // entonces el vehiculo en cuestion debera pararse y arrancar hasta un tiempo delay despues.

                                        startTime = nextEvent.evTime() + car.getDelayToStart();//ESTO ES PARA SIMULAR EL RETARDO DE ARRANQUE DE UN CONDUCTOR QUE REALMENTE PARO

                                        newEvent = SE;
                                    } else {    // en el caso de que el vehiculo de adelante posiblemente heredo el stop del de adelante. solo hacemos warnings.
                                        // puedes ser WBE o CE

                                        startTime = nextEvent.evTime();
                                        newEvent = WSE;
                                    }

                                }
                                evaluatedPosition = nextEvent.evPosition();
                            }

                        }
                    }
                } else // es un cambio de luz
                {
                    // Se elimina el DIST_MIN_SAFE, para que evalue el tiempo que tarda en llegar
                    // al extremo de la calle ya que al parecer no encontro ningun obstaculo.

                    PosiblearrivalPosition = nextEvent.evPosition();
                    PosibledeltaP = PosiblearrivalPosition - this.evPosition();

                    PosibledeltaT = (PosibledeltaP * 3600) / car.getVelocity();
                    PosiblearrivalTime = PosibledeltaT + this.evTime();
                    //aqui no se necesita un delaytostart pues cuando el coche lllega
                    //no se programa el arranque

                    if (newEvent == EMPTY) // en caso de que el simulador, ya habia definido algun evento con algun elemento de la lista previamente visitado o no
                    {
                        if (PosiblearrivalTime < nextEvent.evTime())//SI EL VEHICULO LLEGA ANTES DEL EVENTO
                        {
                            if (nextEvent.getDesiredStatus() == VERDE) {
                                //DEBE ESTAR PARADO ESE TIEMPO HASTA QUE SE PONGA EN VERDE.
                                newEvent = SE;
                                arrivalPosition = PosiblearrivalPosition - car.getDisMinSafe();
                                deltaP = arrivalPosition - this.evPosition();
                                deltaT = (deltaP * 3600) / car.getVelocity();
                                arrivalTime = deltaT + this.evTime();
                                startTime = nextEvent.evTime() + car.getDelayToStart(); //LE agrego delay porque el vehiculo arranca delay unidades despues de que el semaforo se puso en verde

                            } else {
                                if (nextEvent.getDesiredStatus() == ROJO) {
                                    newEvent = CE;
                                    arrivalPosition = PosiblearrivalPosition;
                                    deltaP = PosibledeltaP;
                                    deltaT = PosibledeltaT;
                                    arrivalTime = deltaT + this.evTime();
                                    startTime = arrivalTime;//SI ESTOY EN UN EVENTO DE ARRIVAL
                                //NO OCUPO DELAYTOSTART, PERO SI ESTOY EN UN STOP, SI OCUPO EL DELAYTYOSTART
                                //evaluatedPosition = arrivalPosition;

                                } else {


                                    //2/feb/07 ELN ya no lo vamos a ignorar, se agrega el codigo
                                    if (nextEvent.getDesiredStatus() == AMBAR) {
                                        newEvent = CE;
                                        arrivalPosition = PosiblearrivalPosition;
                                        deltaP = PosibledeltaP;
                                        deltaT = PosibledeltaT;
                                        arrivalTime = deltaT + this.evTime();
                                        startTime = arrivalTime;//SI ESTOY EN UN EVENTO DE ARRIVAL
                                    //NO OCUPO DELAYTOSTART, PERO SI ESTOY EN UN STOP, SI OCUPO EL DELAYTYOSTART
                                    //evaluatedPosition = arrivalPosition;

                                    }

                                }
                            }
                        } else //SI EL VEHICULO LLEGA DESPUES DEL EVENTO.
                        {
                            if (nextEvent.getDesiredStatus() == VERDE) {
                                newEvent = CE;
                                arrivalPosition = PosiblearrivalPosition;
                                deltaP = PosibledeltaP;
                                deltaT = PosibledeltaT;
                                arrivalTime = deltaT + this.evTime();
                                startTime = arrivalTime;
                            //evaluatedPosition = arrivalPosition;
                            } else {
                                if (nextEvent.getDesiredStatus() == ROJO) {
                                    /*SE AGREGA EL TIPO DE EVENTO WE (WARNING EVENT)
                                     * PARA REPRESENTAR LOS STOPS QUE SON SOLO DE ALERTA Y NO
                                     * PROVOCA EN EL AUTOMOVIL QUE SE PARE REALMENTE
                                     * */
                                    newEvent = SE;

                                    //AQUI NO PONGO STARTTIME, PORQUE COMO ES STOP NO SE CUANDO VAYA A ACABAR EL STOP.
                                    // porque llego y y me paro e inicio en ese momento para ver que voy a hacer.
                                    // es que no se cuando va a acabar el stop.
                                    arrivalPosition = PosiblearrivalPosition - car.getDisMinSafe();
                                    deltaP = arrivalPosition - this.evPosition();
                                    deltaT = (deltaP * 3600) / car.getVelocity();
                                    arrivalTime = deltaT + this.evTime();
                                    startTime = arrivalTime;
                                //evaluatedPosition = arrivalPosition;
                                } else {
                                    //ignorarlo es amariollo
									/*
                                     * 2/FEB/06 eln. AQUI SE agrega el bloque para tomar encuenta al ambar.
                                     */
                                    newEvent = CE;
                                    arrivalPosition = PosiblearrivalPosition;
                                    deltaP = PosibledeltaP;
                                    deltaT = PosibledeltaT;
                                    arrivalTime = deltaT + this.evTime();
                                    startTime = arrivalTime;
                                //evaluatedPosition = arrivalPosition;

                                }
                            }
                        }
                    } else // SI YA SE HABIA DEFINIDO UN EVENTO A EJECUTAR POR EL VEHICULO
                    {

                        if (newEvent == CE) {
                            // Si el arrival time del evento que ya tengo predefinido es menor al evento que estoy viendo
                            if (PosiblearrivalTime > nextEvent.evTime()) {

                                //aqui a la mejor podria verificar si voy a llegar a un verde o rojo,
                                // aqui estoy suponiendo es rojo oks.
        						/*calcular a que hora cruzaria si es que cruza AQUI
                                 * NO DEBE CRUZAR YA QUE HAY UN STOP EN EL SIGUIENTE*/

                                if (nextEvent.getDesiredStatus() == ROJO) {
                                    // Si el estado deseado del siguiente evento es rojo que pase a un stop event y si es un ambar no hace nada, lo
                                    //sigue dejando en CE.

                                    arrivalPosition = nextEvent.evPosition() - car.getDisMinSafe();
                                    deltaP = arrivalPosition - this.evPosition();
                                    deltaT = (deltaP * 3600) / car.getVelocity();
                                    arrivalTime = deltaT + this.evTime();
                                    startTime = arrivalTime;
                                    //evaluatedPosition = arrivalPosition;
                                    newEvent = SE;//es stop porque estoy llegando despues del rojo

                                }
                            }
                        } else {


                            if ((newEvent == SE)) // Solo nos interesa el stop, ya que los warnings no se deben suplantar por 
                            //algun otro evento, por eso quitamos todos los warnings de esta comparaci�n
                            {

                                if (PosiblearrivalTime > nextEvent.evTime()) {

                                    /*
                                     *07/feb/07 eln. para que coloque el ce solo cuando el sem�foro sea ambar
                                     *o verde.
                                     */
                                    if (nextEvent.getDesiredStatus() == VERDE || nextEvent.getDesiredStatus() == AMBAR) {
                                        arrivalPosition = nextEvent.evPosition();
                                        deltaP = arrivalPosition - this.evPosition();
                                        deltaT = (deltaP * 3600) / car.getVelocity();
                                        arrivalTime = deltaT + this.evTime();
                                        if (newEvent == SE)//zaz 130207
                                        {
                                            startTime = arrivalTime + car.getDelayToStart();
                                        } else {
                                            startTime = arrivalTime;
                                        }
                                        //	evaluatedPosition = arrivalPosition;
                                        newEvent = CE;
                                    }
                                } else { // El vehiculo llegar� antes que se de el evento de cambio de luz
                                    //zaz 130207 agrego codigo para que no se quede trabado en el WSE hasta que se pone rojo y entonces avanza

                                    if (startTime < PosiblearrivalTime) { // el vehiculo hiba a arrancar antes de que llegara a la posici�n del semaforo?
                                        /// if es verde o ambar

                                        if (nextEvent.getDesiredStatus() == ROJO || nextEvent.getDesiredStatus() == AMBAR) {
                                            arrivalPosition = nextEvent.evPosition();
                                            deltaP = arrivalPosition - this.evPosition();
                                            deltaT = (deltaP * 3600) / car.getVelocity();
                                            arrivalTime = deltaT + this.evTime();
                                            if (newEvent == SE) {
                                                startTime = arrivalTime + car.getDelayToStart();
                                            } else {
                                                startTime = arrivalTime;
                                            }
                                            //	evaluatedPosition = arrivalPosition;
                                            newEvent = CE;
                                        } else {
                                            startTime = nextEvent.evTime();
                                        }

                                    }
                                }
                            }
                        }
                    }//Else no es empty

                }//ELSE cambio de luz

                nextEvent = nextEvent.siguiente;
            }


            if (newEvent == EMPTY) {

                arrivalPosition = segment.getSize();
                deltaP = arrivalPosition - this.evPosition();
                deltaT = (deltaP * 3600) / car.getVelocity();
                arrivalTime = deltaT + this.evTime();

                car.setTypeSeq(CE);
                car.setBegEvTime(this.evTime());
                car.setEndEvTime(arrivalTime);
                car.setCrossDeltaP(deltaP);
                car.setStopTime(arrivalTime);

                startTime = arrivalTime;
                newEvent = CE;
            // System.out.println(car.getId() + "arrivalTime " + arrivalTime + " startTime "+startTime);


            }
            if (newEvent == CE && !this.segment.isPozo()) //Ojo esta es otra condici�n que es unica para un arrival.
            {
                // falta que compare si es interseccion o calle, por default ahorita dice que llega solo a calles.
                // posiblemente algo como sigue:
                            /*
                 */
                /*     Segment segmentOut = null;
                if (segment instanceof Street) {
                Street street = (Street) segment;
                segmentOut = street.getOut();
                
                }
                if (segment instanceof Intersection) {
                Intersection intersection = (Intersection) segment;
                segmentOut = intersection.randomOut();
                
                }
                
                 */


                // vamos a suponer que nunca esta lleno, creo que aqui es el problema cuando paralelizo.
                //if (segmentOut.isFull(startTime)) {
                // throw new UnsupportedOperationException("Not yet implemented if the other segment is full");
                if (false) {

                    newEvent = WSE;
                    arrivalPosition = segment.getSize() - car.getDisMinSafe();
                    deltaP = arrivalPosition - this.evPosition();
                    deltaT = (deltaP * 3600) / car.getVelocity();
                    arrivalTime = deltaT + this.evTime();
                    startTime = arrivalTime;


                    myLogger.debug(car.getId() + " coloco un  Warning Stop Event, ya que hiba a cruzar pero estaba lleno");



                } else {

                    car.setTypeSeq(CE);
                    car.setBegEvTime(this.evTime());
                    car.setEndEvTime(arrivalTime);
                    car.setCrossDeltaP(deltaP);
                    car.setStopTime(arrivalTime);


                    myLogger.debug(car.getId() + " coloco un  " + CE + " Y UN " + ALE + "a las " + arrivalTime + " y  " + startTime + " en pos " + arrivalPosition);


                    this.myStreetControllerAgent.putEvent(this.segment.getId(), arrivalTime, CE, car, arrivalPosition, AMBAR, this.lane);
                    //segment.agregarEvento(arrivalTime,CE,car,arrivalPosition,3,this.lane); //leavelink
                    this.myStreetControllerAgent.putEvent(this.segment.getConnections(), startTime, ALE, car, 0.0, AMBAR, this.lane);
                //segmentOut.agregarEvento(startTime,ALE,car,0.0,3,this.lane); // agrega el evento en el segmento siguiente


                }//else not is full

            }// new event not empty

            if ((newEvent == CE) && this.segment.isPozo())// La calle en la que estoy es un pozo.
            {

                arrivalPosition = segment.getSize();
                deltaP = arrivalPosition - this.evPosition();

                deltaT = (deltaP * 3600) / car.getVelocity();

                arrivalTime = deltaT + this.evTime();




                car.setTypeSeq(CE);
                car.setBegEvTime(evTime());
                car.setEndEvTime(arrivalTime);
                car.setCrossDeltaP(deltaP);
                car.setStopTime(arrivalTime);


                startTime = arrivalTime;

                //el cuatro significa que ya se murio 
                this.myStreetControllerAgent.putEvent(this.segment.getId(), arrivalTime, CE, car, arrivalPosition, 4, this.lane);
            //segment.agregarEvento(arrivalTime,CE,car,arrivalPosition,3,this.lane); //leavelink
            // new CarLeaveLink().schedule(arrivalTime,CE,objectId(),arrivalPosition,segmentoPrioritario,segmentoPrioritario);






            }
            if (newEvent == SE || newEvent == WSE) {
                if (newEvent == SE) //car.typeSeq =SE;
                {
                    car.setTypeSeq(SE);
                } else //car.typeSeq =WSE;
                {
                    car.setTypeSeq(WSE);
                }

                car.setBegEvTime(evTime());
                car.setEndEvTime(startTime);
                car.setStopDeltaP(deltaP);
                car.setStopTime(arrivalTime);

                if (newEvent == SE) {

                    myLogger.debug(car.getId() + " coloco un  " + SE + " Y UN " + BE + "a las " + arrivalTime + " y  " + startTime + " en pos " + arrivalPosition);


                    this.myStreetControllerAgent.putEvent(this.segment.getId(), arrivalTime, SE, car, arrivalPosition, AMBAR, this.lane);
                    this.myStreetControllerAgent.putEvent(this.segment.getId(), startTime, BE, car, arrivalPosition, AMBAR, this.lane);

                //segment.agregarEvento(arrivalTime,SE,car,arrivalPosition,3,this.lane); //CarStop
                //segment.agregarEvento(startTime,BE,car,arrivalPosition,3,this.lane); //CarStart

                // new CarStop().schedule(arrivalTime,SE,objectId(),arrivalPosition,segmentoPrioritario,segmentoPrioritario);
                //  new CarStart().schedule(startTime,BE,objectId(),arrivalPosition,segmentoPrioritario,segmentoPrioritario);


                } else {


                    myLogger.debug(car.getId() + " coloco un  " + WSE + " Y UN " + WBE + "a las " + arrivalTime + " y  " + startTime + " en pos " + arrivalPosition);


                    this.myStreetControllerAgent.putEvent(this.segment.getId(), arrivalTime, WSE, car, arrivalPosition, AMBAR, this.lane);
                    this.myStreetControllerAgent.putEvent(this.segment.getId(), startTime, WBE, car, arrivalPosition, AMBAR, this.lane);



                }



            }


        } else {
            //Pendiente hay que ver que hacer con los vehiculos que son rechazados por que ya esta llena la calle.
            myLogger.debug("Esta lleno el segmento " + this.segment.getId() + "El carro " + car.getId() + " se ha desechado");
        }

        myLogger.debug("finishing At: " + this.evTime() + " event type:  " + this.getEventType());
        myStreetControllerAgent.setReady();


    }
}
